﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Cysharp.Threading.Tasks;
using GraphProcessor;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Logic;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEditor.Timeline;
using UnityEngine;
using UnityEngine.Playables;
using Random = System.Random;

namespace IQIGame.Onigao.GamePlay.Editors
{
    public class SkillEditorWindow : EditorWindow
    {
        private static SkillEditorWindow instance;

        public static SkillEditorWindow Instance
        {
            get
            {
                if (instance == null)
                {
                    var rOldWindow = EditorWindow.GetWindow<SkillEditorWindow>();
                    if (rOldWindow == null)
                    {
                        instance = EditorWindow.CreateWindow<SkillEditorWindow>("SkillEditor");
                    }
                    else
                    {
                        instance = rOldWindow;
                    }
                }
                return instance;
            }
        }

        private const string scenePath = "Assets/EditorConfig/Scenes/SkillEditor.unity";

        [MenuItem("ToolApp/技能编辑器/编辑器", false, 301)]
        public static void Open()
        {
            Instance.Show();
            // 换场景
            EditorSceneManager.OpenScene(scenePath);
        }

#region Window

        private void OnEnable()
        {
            if (!Application.isPlaying)
            {
                BattleEffectManager.Instance.Enable();
            }
        }

        private void OnDisable()
        {
            if (!Application.isPlaying)
            {
                BattleEffectManager.Instance.Disable();
            }
        }

        private void OnInspectorUpdate()
        {
            Repaint();
        }

        private Transform roleRoot;
        private Transform enemyRoot;

        private UnitView unitView;
        public UnitView enemyView;

        private bool pause;
        private List<(string, EUnitType)> allroleData;
        private int selectRoleIndex = 0;
        private int enemyRoleIndex = 0;
        private string[] roleList;

        private int skillIndex = 0;
        private string[] skillList;
        private int selectSkill;

        private int partIndex = 0;
        private string[] skillParts;
        private SkillPartConfig[] parts;

        private void OnGUI()
        {
            if (Application.isPlaying)
            {
                EditorGUILayout.LabelField("仅在编辑模式下启动");
                return;
            }

            if (EditorSceneManager.GetActiveScene().path != scenePath)
            {
                EditorGUILayout.LabelField("仅在技能编辑场景中可用");
                if (BattleEditorTools.Button("跳转到编辑场景"))
                {
                    EditorSceneManager.OpenScene(scenePath);
                }
                return;
            }

            if (!this.roleRoot)
            {
                this.roleRoot = GameObject.Find("RoleRoot").transform;
            }
            if (!this.enemyRoot)
            {
                this.enemyRoot = GameObject.Find("Enemy").transform;
            }

            if (BattleEditorTools.Button("加载配置"))
            {
                SkillFactory.SetLoadFunc((path) => { return SkillViewUtil.Load(path); });
                this.allroleData?.Clear();
                this.TryLoadConfig().Forget();
            }
            if (TableCenter.hero == null || this.roleList == null || this.roleList.Length <= 0 || this.allroleData == null || this.allroleData.Count <= 0)
            {
                EditorGUILayout.LabelField("配置表未加载");
                return;
            }

            bool bUnitChnage = this.unitView == null || this.unitView.configID.ToString() != this.roleList[this.selectRoleIndex] ||
                               this.enemyView == null || this.enemyView.configID.ToString() != this.roleList[this.enemyRoleIndex];
            GUI.color = bUnitChnage ? Color.red : Color.white;
            GUILayout.BeginHorizontal();
            this.selectRoleIndex = EditorGUILayout.Popup("攻击方：", this.selectRoleIndex, this.roleList);
            this.enemyRoleIndex = EditorGUILayout.Popup("受击方：", this.enemyRoleIndex, this.roleList);
            GUILayout.EndHorizontal();
            GUI.color = Color.white;
            if (BattleEditorTools.Button("加载单位"))
            {
                this.LoadUnit().Forget();
            }
            if (this.unitView == null || this.skillList == null || this.skillList.Length <= 0)
            {
                return;
            }

            EditorGUILayout.LabelField("技能：");

            this.skillIndex = EditorGUILayout.Popup(this.skillIndex, this.skillList);
            if (BattleEditorTools.Button("重新加载技能"))
            {
                this.selectSkill = 0;
                SkillFactory.ClearConfigCache();
            }
            if (this.selectSkill.ToString() != this.skillList[this.skillIndex])
            {
                this.selectSkill = int.Parse(this.skillList[this.skillIndex]);
                var rConfig = SkillFactory.GetConfig(this.selectSkill);
                this.parts = rConfig.parts.ToArray();
                this.skillParts = this.parts.Select((x) =>
                {
                    if (x.partType == (int)ESkillPartType.Main)
                    {
                        return "Main: " + x.name;
                    }
                    return x.name;
                }).ToArray();
            }
            if (this.skillParts == null || this.skillParts.Length <= 0)
            {
                return;
            }
            this.partIndex = EditorGUILayout.Popup(this.partIndex, this.skillParts);
            if (this.parts == null || this.parts.Length <= 0)
            {
                return;
            }
            {
                EditorGUILayout.BeginHorizontal();
                if (BattleEditorTools.Button("播放Timeline"))
                {
                    this.pause = false;
                    var rPart = this.parts[this.partIndex];
                    var rTimeline = rPart.timeline;
                    if (string.IsNullOrEmpty(rTimeline))
                    {
                        LogGame.Log("此Part无Timeline配置");
                    }
                    SkillEditorMainUpdate.Instance.target = this.enemyView;
                    var rTimelineComponent = this.unitView.GetComponent<BattleTimelineComponent>(ETComponentType.BattleTimelineComponent);
                    rTimelineComponent.PlayTimeline(rTimeline, null, OnSignal).Forget();
                }

                if (BattleEditorTools.Button("技能配置"))
                {
                    var nSkillID = this.selectSkill;
                    var rTabConfig = TableCenter.skill.Get(nSkillID);
                    var rPath = "Assets/EditorConfig/SkillConfig/" + rTabConfig.ResFloder + "/" + rTabConfig.ResName + ".asset";
                    var rAsset = AssetDatabase.LoadAssetAtPath<BaseGraph>(rPath);
                    EditorWindow.GetWindow<SkillEditorGraphWindow>().InitializeGraph(rAsset);
                }

                if (BattleEditorTools.Button("timeline"))
                {
                    var rTimelineComponent = this.unitView.GetComponent<BattleTimelineComponent>(ETComponentType.BattleTimelineComponent);
                    //EditorApplication.ExecuteMenuItem("Window/Sequencing/Timeline");
                    var rTimelineWindow = TimelineEditorWindow.GetWindow<TimelineEditorWindow>();
                    rTimelineWindow.SetTimeline(this.unitView.GetComponent<UnitViewComponent>(ETComponentType.UnitViewComponent).gameObject.GetComponent<PlayableDirector>());
                    rTimelineWindow.Show();
                    //BattleClientUtil.DelayInvoke(20, () => { Selection.activeObject = this.unitView.GetComponent<UnitViewComponent>(ETComponentType.UnitViewComponent).gameObject; }).Forget();
                }
                EditorGUILayout.EndHorizontal();
            }
            SkillEditorMainUpdate.Instance.showCameraCurve = EditorGUILayout.Toggle("播放技能相机动画", SkillEditorMainUpdate.Instance.showCameraCurve);
            if (BattleEditorTools.Button(this.pause ? "继续" : "暂停子弹"))
            {
                this.pause = !this.pause;
            }
            if (this.pause && BattleEditorTools.Button("Step"))
            {
                this.Step();
            }
        }

        // public 

        private Dictionary<string, Action> effectCallback = new Dictionary<string, Action>();

        private void OnSignal(UnitView unit, ExSignalEmitterData signalData)
        {
            switch (signalData.type)
            {
                case EExSignalType.SkillEffect:
                {
                    var rType = (ESignalSkillEffectType)signalData.paramInt[0];
                    switch (rType)
                    {
                        case ESignalSkillEffectType.Normal:
                            OnHit();
                            break;
                        case ESignalSkillEffectType.Bullet:
                            var rBulletComp = unit.GetComponent<BulletComponent>(ETComponentType.BulletComponent);
                            var rIntArr = signalData.paramInt;
                            var rStartDeviation = new Vector3(rIntArr.Get(4) / 100f, rIntArr.Get(5) / 100f, rIntArr.Get(6) / 100f);
                            var rEndDeviation = new Vector3(rIntArr.Get(7) / 100f, rIntArr.Get(8) / 100f, rIntArr.Get(9) / 100f);
                            var nEffectRandomCount = rIntArr.Get(10);
                            var rEffectName = signalData.paramStr.Get(0);
                            if (nEffectRandomCount > 1)
                            {
                                var nEffectIndex = UnityEngine.Random.Range(0, nEffectRandomCount);
                                rEffectName = signalData.paramStr.Get(nEffectIndex);
                            }
                            rBulletComp.AddBulletToTarget(rEffectName, rIntArr.Get(1), rStartDeviation,
                                rEndDeviation, this.enemyView, rIntArr.Get(2), rIntArr.Get(3) / 1000f, OnHit);
                            break;
                        case ESignalSkillEffectType.Delay:
                        {
                            var rTargetEffect = signalData.paramStr.Get(0);
                            var nDummy = signalData.paramInt.Get(1);
                            if (!string.IsNullOrEmpty(rTargetEffect))
                            {
                                var rAttackerEffectComp = unit.GetComponent<EffectComponent>(ETComponentType.EffectComponent);
                                var rEffectPath = rAttackerEffectComp.GetRoleEffectPath(rTargetEffect);
                                var rBindComp = this.enemyView.GetComponent<BattleViewBindPointComponent>(ETComponentType.BattleViewBindPointComponent);
                                var rInitParam = new EffectInitParam(rEffectPath, rBindComp.GetBindPoint(nDummy), Vector3.zero, Quaternion.identity,
                                    rBindComp, vfxSpace: EVFXSpace.Local);
                                BattleEffectManager.Instance.PlayEffect(rInitParam, 0);
                            }
                            var fDelay = signalData.paramInt.Get(2);
                            BattleClientUtil.DelayInvoke(fDelay, OnHit).Forget();
                            break;
                        }
                        case ESignalSkillEffectType.WaitCallback:
                        {
                            this.effectCallback.Add(signalData.paramStr.Get(0), this.OnHit);
                            break;
                        }
                    }
                    break;
                }
                case EExSignalType.Callback:
                {
                    effectCallback.TryGetValue(signalData.paramStr.Get(0), out var rAction);
                    rAction?.Invoke();
                    break;
                }
                case EExSignalType.FakeHit:
                {
                    OnHit((EDamageType)(1 << signalData.paramInt.Get(0)));
                    break;
                }
            }
        }

        private void OnHit()
        {
            this.OnHit(EDamageType.None);
        }

        private void OnHit(EDamageType damageType)
        {
            var rTabConfig = TableCenter.skill.Get(this.selectSkill);
            if (damageType == EDamageType.None)
            {
                damageType = (EDamageType)(1 << rTabConfig.DamageType);
            }
            var rEffectName = this.unitView.GetComponent<EffectComponent>(ETComponentType.EffectComponent).GetHitEffectBySkill(damageType, this.selectSkill);
            var rEnemyViewBindPointComp = this.enemyView.GetComponent<BattleViewBindPointComponent>(ETComponentType.BattleViewBindPointComponent);
            var rInitParam = new EffectInitParam(rEffectName, rEnemyViewBindPointComp.GetBindPoint(2),
                Vector3.zero, Quaternion.identity, vfxSpace: EVFXSpace.Local);

            BattleEffectManager.Instance.PlayEffect(rInitParam);

            var rViewComp = this.enemyView.GetComponent<UnitViewComponent>(ETComponentType.UnitViewComponent);
            rViewComp.PlayAnime(AnimationConst.Name_Hit);
        }

        private static double preTime;
        private static double delta;

        public void Update()
        {
            if (SkillEditorMainUpdate.Instance && SkillEditorMainUpdate.Instance.OnUpdate == null)
            {
                SkillEditorMainUpdate.Instance.OnUpdate = this.OnUpdate;
            }
        }

        private void OnUpdate()
        {
            if (Application.isPlaying)
            {
                return;
            }
            if (EditorSceneManager.GetActiveScene().path != scenePath)
            {
                return;
            }


            var fCurTime = System.Environment.TickCount / 1000d;
            delta = fCurTime - preTime;
            preTime = fCurTime;

            if (this.pause)
            {
                return;
            }
            this.Step();
        }

        private void Step()
        {
            if (this.unitView != null)
            {
                this.unitView.OnUpdate((float)delta);
            }

            BattleEffectManager.Instance.Update((float)delta * Time.timeScale, (float)delta);
        }

        private async UniTaskVoid TryLoadConfig()
        {
            await TableCenter.LoadTables();
            if (this.allroleData == null || this.allroleData.Count <= 0)
            {
                this.allroleData = new List<(string, EUnitType)>();
                foreach (var rConfig in TableCenter.hero.DataList)
                {
                    this.allroleData.Add((rConfig.Id.ToString(), EUnitType.Hero));
                }
                foreach (var rConfig in TableCenter.monster.DataList)
                {
                    this.allroleData.Add((rConfig.Id.ToString(), EUnitType.Monster));
                }
                this.roleList = this.allroleData.Select((x) => x.Item1).ToArray();
            }
        }

        private async UniTaskVoid LoadUnit()
        {
            this.Reset();
            foreach (Transform rTrans in this.roleRoot)
            {
                GameObject.DestroyImmediate(rTrans.gameObject);
            }
            foreach (Transform rTrans in this.enemyRoot)
            {
                GameObject.DestroyImmediate(rTrans.gameObject);
            }

            var nID = int.Parse(this.roleList[this.selectRoleIndex]);
            var nEnemyID = int.Parse(this.roleList[this.enemyRoleIndex]);
            if (this.allroleData[this.selectRoleIndex].Item2 == EUnitType.Hero)
            {
                this.unitView = UnitEditorFactory.CreateEditorViewHero(nID, TableCenter.hero);
            }
            else
            {
                this.unitView = UnitEditorFactory.CreateEditorViewMonster(nID, TableCenter.monster);
            }
            if (this.allroleData[this.enemyRoleIndex].Item2 == EUnitType.Hero)
            {
                this.enemyView = UnitEditorFactory.CreateEditorViewHero(nEnemyID, TableCenter.hero);
            }
            else
            {
                this.enemyView = UnitEditorFactory.CreateEditorViewMonster(nEnemyID, TableCenter.monster);
            }

            var rUnitViewComp = this.unitView.GetComponent<UnitViewComponent>(ETComponentType.UnitViewComponent);
            await rUnitViewComp.Initialize();
            this.unitView.GetComponent<BattleAnimationComponent>(ETComponentType.BattleAnimationComponent).Initialize();
            this.unitView.GetComponent<BattleTimelineComponent>(ETComponentType.BattleTimelineComponent).Initialize();
            var rSkillComp = this.unitView.GetComponent<SkillViewComponent>(ETComponentType.SkillViewComponent);
            rSkillComp.OnEnable();
            rUnitViewComp.transform.SetParent(this.roleRoot);
            rUnitViewComp.transform.localPosition = Vector3.zero;
            rUnitViewComp.Enable();

            var rEnemyViewComp = this.enemyView.GetComponent<UnitViewComponent>(ETComponentType.UnitViewComponent);
            await rEnemyViewComp.Initialize();
            this.enemyView.GetComponent<BattleAnimationComponent>(ETComponentType.BattleAnimationComponent).Initialize();
            this.enemyView.GetComponent<BattleTimelineComponent>(ETComponentType.BattleTimelineComponent).Initialize();
            rEnemyViewComp.transform.SetParent(this.enemyRoot);
            rEnemyViewComp.transform.localPosition = Vector3.zero;
            rEnemyViewComp.transform.localRotation = Quaternion.identity;
            rEnemyViewComp.Enable();

            this.skillList = rSkillComp.allSkills.Select((x) => x.ToString()).ToArray();
        }

        private void Reset()
        {
            this.unitView = null;
            this.skillIndex = 0;
            this.skillList = default;
            this.selectSkill = default;
            this.partIndex = 0;
            this.skillParts = default;
            this.parts = default;
        }

#endregion

        private const string directoryPath = "Assets/EditorConfig/SkillConfig";
        private const string preview = "Preview";

        [MenuItem("ToolApp/技能编辑器/全量导出", false, 311)]
        public static void ExportAllConfig()
        {
            ExportConfig(false);
        }

        [MenuItem("ToolApp/技能编辑器/批量导出含预览(根据选中可以只导出单个文件夹中)", false, 312)]
        public static void ExportAllConfigWithPreview()
        {
            ExportConfig(true);
        }

        [MenuItem("ToolApp/技能编辑器/清理预设", false, 313)]
        public static void ClearPrefab()
        {
            SkillEditorBlackboard.Instance.ClearPrefab();
        }

        [MenuItem("ToolApp/技能编辑器/修改BaseGraph为SkillEditorGraph", false, 314)]
        public static void BaseGraph2SkillEditorGraph()
        {
            var rDirPath = "Assets/EditorConfig/SkillConfig";
            var rDirs = Directory.GetDirectories(rDirPath);
            foreach (var rDir in rDirs)
            {
                var rFiles = Directory.GetFiles(rDir);
                foreach (var rFile in rFiles)
                {
                    if (!rFile.EndsWith("asset"))
                    {
                        continue;
                    }
                    var rTxt = File.ReadAllText(rFile);
                    // 替换basegraph为skilleditorgraph
                    rTxt = rTxt.Replace("m_Script: {fileID: 11500000, guid: 31390c44c9f3a3d40b424bf6821999a2, type: 3}",
                        "m_Script: {fileID: 11500000, guid: 316b498fa3fb3c24081799ad5ed9306f, type: 3}");
                    File.WriteAllText(rFile, rTxt);
                }
            }
            AssetDatabase.Refresh();
        }

        private static void ExportConfig(bool bPreview)
        {
            SkillEditorBlackboard.guidCheck.Clear();
            var rSelect = Selection.activeObject;
            var rTargetDir = string.Empty;
            // 有预览的情况才会有导出部分文件夹
            if (rSelect != null && bPreview)
            {
                string path = AssetDatabase.GetAssetPath(rSelect);
                if (!string.IsNullOrEmpty(path) && path != directoryPath)
                {
                    var rDirFull = Path.GetFullPath(directoryPath);
                    var rPathFull = Path.GetFullPath(path);
                    if (rPathFull.StartsWith(rDirFull))
                    {
                        if (!Directory.Exists(path))
                        {
                            path = Path.GetDirectoryName(path);
                        }

                        rTargetDir = Path.GetFileName(path);
                    }
                }
            }

            if (string.IsNullOrEmpty(rTargetDir))
            {
                // 全量导出，删除prefab重新导出
                File.Delete(SkillEditorBlackboard.PrefabBytesPath);
                File.Delete(SkillEditorBlackboard.PrefabJsonPath);
            }

            var rDir = Directory.GetDirectories(directoryPath);
            for (int i = 0; i < rDir.Length; i++)
            {
                var rPath = rDir[i];
                if (rPath.Contains(preview) || (!string.IsNullOrEmpty(rTargetDir) && Path.GetFileName(rPath) != rTargetDir))
                {
                    continue;
                }

                var rFiles = Directory.GetFiles(rPath);

                for (int j = 0; j < rFiles.Length; j++)
                {
                    if (rFiles[j].Contains(".meta"))
                    {
                        continue;
                    }
                    var graph = AssetDatabase.LoadAssetAtPath<BaseGraph>(rFiles[j]);
                    if (graph != null)
                    {
                        for (int k = 0; k < graph.nodes.Count; k++)
                        {
                            graph.nodes[k].Clear();
                        }
                        var rSortList = graph.nodes.OrderBy(n => n.computeOrder).ToList();
                        for (int k = 0; k < rSortList.Count; k++)
                        {
                            var rNode = rSortList[k];
                            rNode.OnProcess();
                        }

                        SkillEditorBlackboard.Instance.Package(rFiles[j], bPreview, true);
                    }
                }
            }
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
        }
    }
}